/*-
 * Copyright (c) 2008-2010, Oleg Estekhin
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the distribution.
 *  * Neither the names of the copyright holders nor the names of their
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

package oe.assertions.predicates;

import oe.assertions.MessageBuffer;
import oe.assertions.Predicate;

/** The base class for binary predicates that operate on {@code Number}s. */
public abstract class BinaryNumberPredicate<T extends Number> implements Predicate<T> {

    protected final T target;


    /**
     * Creates a new predicate.
     *
     * @param target the target value
     */
    protected BinaryNumberPredicate( T target ) {
        this.target = target;
    }


    @Override
    public boolean check( T input, MessageBuffer message ) {
        boolean result;
        if ( input instanceof Double || target instanceof Double ) {
            result = input instanceof Double && target instanceof Double && checkDouble( ( Double ) input, ( Double ) target );
        } else if ( input instanceof Float || target instanceof Float ) {
            result = input instanceof Float && target instanceof Float && checkFloat( ( Float ) input, ( Float ) target );
        } else if ( input instanceof Number || target instanceof Number ) {
            // todo special treatment for BigInteger and BigDecimal
            result = input instanceof Number && target instanceof Number && checkNumber( ( Number ) input, ( Number ) target );
        } else {
            result = checkObject( input, target );
        }
        if ( !result ) {
            MessageBuffer predicateMessage = new MessageBuffer( message, message.getIndentationLevel() + 1 );
            describe( predicateMessage );
            predicateMessage.publish();
            predicateMessage.dispose();
        }
        return result;
    }


    protected abstract boolean checkDouble( double input, double target );

    protected abstract boolean checkFloat( float input, float target );

    protected abstract boolean checkNumber( Number input, Number target );

    protected abstract boolean checkObject( Object input, Object target );

    protected abstract void describe( MessageBuffer message );

}
